Odeeen CPUを実機で動かす
#自作CPU #RISC-V #Verilog #ULX3S #FPGA
from マルチサイクル RISC-V CPU を作成したい
ULX3Sで動かす
以下のようなTOPモジュールを用意する。
現状、CPUからアクセス可能な周辺機器が何ひとつ無いので、アクセス中のメモリの番地をLEDへ出力する。
こんな感じ。
assign led = mem_addr[7:0]; // アクセス中のメモリアドレスをLEDに表示する
code:ulx3s_top.sv
//------------------------------------------------------------------
// ULX3S 上で Odeeen CPU を動かすためのトップモジュール
//------------------------------------------------------------------
module ulx3s_top(
input wire clk_25mhz,
input wire 6:0 btn,
output wire 7:0 led
);
logic clk, reset_n;
logic mem_valid, mem_ready, mem_instr;
logic 31:0 mem_addr, mem_wdata, mem_rdata;
logic 3:0 mem_wstrb;
// 外部信号線との接続
assign clk = clk_25mhz;
assign reset_n = btn0; // btn0 は押すとデアサートされる
assign led = mem_addr7:0; // アクセス中のメモリアドレスをLEDに表示する
// CPU のインスタンス
cpu cpu_inst(
.clk(clk),
.reset_n(reset_n),
.mem_valid(mem_valid),
.mem_instr(mem_instr),
.mem_ready(mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata)
);
// メモリコントローラのインスタンス
bram_controller ram (
.clk(clk),
.reset_n(reset_n),
.mem_valid(mem_valid),
.mem_ready(mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata)
);
endmodule
code:Makefile
.PHONY: clean prog test
all: ulx3s.bit
clean:
rm -rf odeeen.json ulx3s_out.config ulx3s.bit
ulx3s.bit: ulx3s_out.config
ecppack ulx3s_out.config ulx3s.bit
ulx3s_out.config: odeeen.json
nextpnr-ecp5 --85k --json odeeen.json --lpf rtl/ulx3s_v20.lpf --textcfg ulx3s_out.config
odeeen.json: rtl/cpu.sv rtl/ulx3s_top.sv rtl/bram_controller.sv
yosys -p "hierarchy -top ulx3s_top" -p "synth_ecp5 -json odeeen.json" rtl/cpu.sv rtl/bram_controller.sv rtl/ulx3s_top.sv
prog: ulx3s.bit
fujprog ulx3s.bit
test:
cd rtl && iverilog -g 2012 -s cpu_test cpu_test.sv cpu.sv bram_controller.sv && ./a.out
アドレス16番地で無限ループするプログラムを bram_controller.sv へ書き込んでおく。
code:bram_controller.sv
`ifndef __INSTRUCTIONS__
`include "instructions.sv"
`endif
// BRAM コントローラー
module bram_controller(
input wire clk,
input wire reset_n,
// TODO: あとで cs (chip select) を追加したい
input wire mem_valid,
output logic mem_ready,
input wire 31:0 mem_addr,
input wire 31:0 mem_wdata,
input wire 3:0 mem_wstrb, // 0'b0000 の場合は読み込み、0'b1111 の場合は書き込み
output logic 31:0 mem_rdata
);
...
// メモリの初期化
initial begin
mem0 = add(0, 0, 0); // nop
mem1 = add(0, 0, 0); // nop
mem2 = add(0, 0, 0); // nop
mem3 = add(0, 0, 0); // nop
mem4 = jal(0, 0); // jal x0, 0 (無限ループ)
end
...
endmodule
以下のコマンドでULX3Sへアップロード。
code:sh
make prog
16番地で無限ループしてるのが確認できる。
https://gyazo.com/629615f0434e74e7ae9b083a8ad3f3bd